package org.bing.engine.utility.codec;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class PrimitiveJsonDecoder extends PrimitiveJsonBase {

    public Object decode(String src) {
        // {null}
        if (src == null || nullString.equals(src)) {
            return null;
        }
        // decode list, {[{...},...{...}]};
        if (src.charAt(1) == '[') {
            return decodeList(src);
        } else { // decode simple
            return decodeObject(src);
        }
    }

    public List decodeList(String src) {
        List result = new ArrayList();
        // blank list, {[]} == 4
        if (src.length() == 4) {
            return result;
        }

        int beg = src.indexOf("{", 1);
        int end = findObjectEnd(src, beg);
        while (end > 0) {
            Object obj = decode(src.substring(beg, end + 1));
            result.add(obj);
            beg = end + 2;
            end = findObjectEnd(src, beg);
        }
        return result;
    }

    public Object decodeObject(String src) {
        // {null}, null
        if (src == null || nullString.equals(src)) {
            return null;
        }

        int fldSpltPos = src.indexOf(OBJECT_FIELD_SPLIT);
        // {class:string,value:something}, simple clazz
        String clazz = src.substring(7, fldSpltPos); // 7 = 6('class:') + 1
        if (super.isSupportedType(clazz)) {
            String value = src.substring(fldSpltPos + 7, src.length() - 1); // 7 = ',value:'
            return decodeSimple(clazz, value);
        }

        // {class:domain.User,name:chenbin,group:{class:domain.Group,name:pt,desc:some},age:107}
        SimpleReflectHelper reflect = SimpleReflectHelper.instance(clazz);
        Object result = reflect.buildObject();
        int valSpltPos = src.indexOf(FIELD_VALUE_SPLIT, fldSpltPos);
        while (valSpltPos > 0) {
            String name = src.substring(fldSpltPos + 1, valSpltPos);
            String type = typeHelper.fineClzName(reflect.getFieldType(name));
            int endValuePos = 0;
            if (isSupportedType(type)) {
                // simple filed
                endValuePos = src.indexOf(OBJECT_FIELD_SPLIT, valSpltPos);
                if (endValuePos < 0) {
                    endValuePos = src.indexOf(OBJECT_END, valSpltPos);
                }
                doSetValue(result, name, decodeSimple(type, src.substring(valSpltPos + 1, endValuePos)));
            } else {
                endValuePos = findObjectEnd(src, valSpltPos) + 1;
                doSetValue(result, name, decode(src.substring(valSpltPos + 1, endValuePos)));
            }
            // go next field
            fldSpltPos = src.indexOf(OBJECT_FIELD_SPLIT, endValuePos);
            if (fldSpltPos > 0) {
                valSpltPos = src.indexOf(FIELD_VALUE_SPLIT, fldSpltPos);
            } else {
                break;
            }
        }
        return result;
    }

    /** simple type decode */
    protected Object decodeSimple(String type, String s) {
        if ("string".equals(type)) {
            return s;
        } else if ("date".equals(type)) {
            return new Date(Long.parseLong(s));
        } else if ("null".equals(type)) {
            return null;
        } else if ("int".equals(type)) {
            return Integer.valueOf(s);
        } else if ("long".equals(type)) {
            return Long.valueOf(s);
        } else if ("boolean".equals(type)) {
            return Boolean.parseBoolean(s);
        } else if ("char".equals(type)) {
            return s.charAt(0);
        } else if ("byte".equals(type)) {
            return Byte.valueOf(s);
        } else if ("short".equals(type)) {
            return Short.valueOf(s);
        } else if ("float".equals(type)) {
            return Float.valueOf(s);
        } else if ("double".equals(type)) {
            return Double.valueOf(s);
        } else {
            throw new RuntimeException("Can't decode " + type);
        }
    }

    private Object doSetValue(Object obj, String field, Object value) {
        SimpleReflectHelper helper = SimpleReflectHelper.instance(obj);
        helper.setValue(obj, field, value);
        return obj;
    }

    private int findObjectEnd(String src, int begin) {
        int num = 0;
        char[] chars = src.toCharArray();
        for (int i = begin; i < chars.length; i++) {
            char c = src.charAt(i);
            if (c == OBJECT_BEG) {
                num++;
            } else if (c == OBJECT_END) {
                num--;
                if (num == 0) {
                    return i;
                }
            }
        }
        return -1;
    }
}
